[t:/]$ 지식_

포인터로 장난치다 흔히 범하는 메모리 얼라인 오류.

2007/07/10

VC 계통에서 다음 코드를 보자.

unsigned char ttt[100];  
unsigned char *bp = &ttt[1];  
unsigned int *bpp = (unsigned int *)bp;  

unsigned char 형으로 잡은 포인터를 1 증가 시켜서 unsigned int 형으로 캐스팅 해서 지정하게 하였다.

데이터 덤프를 떠보니 문제가 없었다. 포인터는 정상적으로 사용된다.

ADS와 같은 컴파일러에서 다음 코드를 실험해보자.

unsigned char *eee = (unsigned char *)(PACKET_BUF);  
unsigned int *rrr;  
eee[0] = 0x11;  
eee[1] = 0x22;  
eee[2] = 0x33;  
eee[3] = 0x44;</P>  
eee++;>  
rrr = (unsigned int *)eee;  
*rrr = 0x55667788;  

rrr 포인터는 분명히 PACKET_BUF + 1 번지의 값을 지정하고 있으리라 예상되지만,
메모리덤프를 떠보니 그냥 PACKET_BUF 로 들어갔다.

unsigned int * 포인터는 4바이트 얼라인으로 강제되어 있는 것이다.

아마도 아키텍쳐 + 컴파일러 + 최적화수준 + 컴파일러 옵션의 조합에 따라서 다양한 형태의 버그로 발생할 것이다.

ARM - ADS 환경에서 다음은 모두 같은 결과를 가진다..

unsigned int *aaa = (unsigned int *)0x4000;  
unsigned int *aaa = (unsigned int *)0x4001;  
unsigned int *aaa = (unsigned int *)0x4002;  
unsigned int *aaa = (unsigned int *)0x4003;  

아예 에러가 나면 다행인데 에러도 나질 않으니 대마왕 버그가 될 가능성이 있는 부분이 된다.
프로토콜 관련 프로그래밍을 하다보면 패킷 처리를 위해 포인터 연산을 많이 하게 된다.
보통 구조체를 packed 로 눌러서 쓰다가 얼라인이 꼬이는 경우는 흔하다.

특히 구조체 -> void * 형으로 전달 -> 구조체 캐스팅으로 쓰는 경우에는.

  1. 바이트 오더링 byte ordering 도 주의해야 하고.
  2. 메모리 얼라인 memory align 도 주의해야 한다.

참고로 구조체의 바이트를 packed 꽉꽉 누르지 않을 때는, 혼동을 방지하기 위해서 다음과 같이 더미를 넣는 것도 좋다.

typedef struct {  
    char value_a[2];  
    char dummy[2];  
    int value_b;  
} aaa;  

사실 요즘 IP개념으로 구조적 설계가 된 SoC류 들은 저런 구조체로 레지스터 뭉치를 집어서 쓰는 편인데, 빈 주소를 표시하기 위해서 char reserved[4] 식으로 지정하기도 한다.

이상 끝.





공유하기













[t:/] is not "technology - root". dawnsea, rss